package com.nirvana.generate;

import com.intellij.openapi.ui.Messages;
import com.nirvana.MainPanel;
import com.nirvana.config.PluginConfig;
import com.nirvana.convert.CamelCaseConverter;
import com.nirvana.data.DaoGenerateContext;
import com.nirvana.data.DbFieldInfo;
import com.nirvana.factory.ItemFactory;
import com.nirvana.item.AbstractItem;
import com.nirvana.item.CommonItem;
import com.nirvana.item.TableFieldItem;
import com.nirvana.type.DaoOption;
import com.nirvana.type.ItemType;
import com.nirvana.utils.*;
import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class OrmEntityGenerator implements GenerateInterface {
    public static DaoGenerateContext context;
    private static OrmEntityGenerator INSTANCE;
    private String currTableName;
    private List<String> tables;
    private JTextField filterTextField;

    private OrmEntityGenerator() {
        initFilterTextField();
    }

    public static OrmEntityGenerator getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new OrmEntityGenerator();
        }
        return INSTANCE;
    }

    public void connect() {
        List<String> tables = DbUtil.getTables();
        this.tables = tables.stream().sorted().toList();
        filterTextField.setText("");
        refreshTables("");
        Messages.showInfoMessage("连接成功", "提示信息");
    }

    private void refreshTables(String filterTableName) {
        if (this.tables == null || this.tables.isEmpty()) return;

        clear();
        MainPanel mainPanel = PluginConfig.mainPanel;
        this.tables.stream().filter(tableName -> tableName.toLowerCase().contains(filterTableName.toLowerCase())).forEach(tableName -> {
            JButton tableButton = createTableButton(tableName);
            tableButton.setMaximumSize(new Dimension(245, 30));
            tableButton.setPreferredSize(new Dimension(145, 30));
            mainPanel.ormTablesPanel.add(tableButton);
        });
        UIUtil.refresh(mainPanel.ormTablesPanel);
    }

    private void initFilterTextField() {
        MainPanel mainPanel = PluginConfig.mainPanel;
        filterTextField = mainPanel.filterTableTextField;
        filterTextField.getDocument().addDocumentListener(new DocumentListener() {
            public void insertUpdate(DocumentEvent e) {
                refreshTables(filterTextField.getText());
            }

            public void removeUpdate(DocumentEvent e) {
                refreshTables(filterTextField.getText());
            }

            public void changedUpdate(DocumentEvent e) {
                refreshTables(filterTextField.getText());
            }
        });
    }

    private Map<String, DbFieldInfo> getFieldMap() {
        CommonItem commonItem = ItemFactory.getCommonItem(ItemType.TableFieldItem);
        Map<String, DbFieldInfo> map = new LinkedHashMap<>();
        for (AbstractItem item : commonItem.itemList) {
            if (item instanceof TableFieldItem tableFieldItem) {
                DbFieldInfo dbFieldInfo = tableFieldItem.getDbFieldInfo();
                map.put(dbFieldInfo.getColumnName(), dbFieldInfo);
            }
        }
        return map;
    }

    public void clear() {
        currTableName = null;
        MainPanel mainPanel = PluginConfig.mainPanel;
        ItemFactory.getCommonItem(ItemType.TableFieldItem).clearAll();
        mainPanel.ormTablesPanel.removeAll();
        mainPanel.ormEntityClassNameTextField.setText("");
        UIUtil.refresh(mainPanel.ormTablesPanel);
    }

    private JButton createTableButton(String tableName) {
        JButton button = new JButton(tableName);
        button.addActionListener(e -> {
            ItemFactory.getCommonItem(ItemType.TableFieldItem).tableChange(tableName);
            currTableName = tableName;
            context = getContext();
            MainPanel mainPanel = PluginConfig.mainPanel;
            mainPanel.ormEntityClassNameTextField.setText(CamelCaseConverter.toCamelCase(tableName, false));
            populateOptionsUI(mainPanel);
        });
        return button;
    }

    private static void populateOptionsUI(MainPanel mainPanel) {
        mainPanel.ormBusinessGetEntityByKeyRadioButton.setText("Business生成" + BusinessCodeUtil.getEntityByKeysMethodSign(context.entityName, context.primaryFields) + "方法");

        List<DbFieldInfo> primaryFields = context.primaryFields.stream().findFirst().stream().toList();
        mainPanel.ormBusinessGetEntitiesByKeyRadioButton.setText("Business生成" + BusinessCodeUtil.getEntityByFirstKeyMethodSign(context.entityName, primaryFields) + "方法");
        mainPanel.ormBusinessUpdateEntityRadioButton.setText("Business生成" + BusinessCodeUtil.updateEntityMethodSign(context.entityName, context.lowEntityName) + "方法");
        mainPanel.ormBusinessUpdateEntitiesBatchRadioButton.setText("Business生成" + BusinessCodeUtil.updateEntityBatchMethodSign(context.entityName, context.lowEntityName) + "方法");
    }

    public void generate()
    {
        if (StringUtils.isBlank(currTableName)) {
            Messages.showWarningDialog("请先选择数据库表再生成代码", "生成提示");
            return;
        }

       context = getContext();
        try {
            generateEntity();
            generateDao();
            generateDaoImpl();
            generateFactory();
            generateBusiness();
            Messages.showInfoMessage("生成DAO成功", "生成结果");
            PluginConfig.reloadAllFilesFromDisk();
        } catch (IOException e) {
            Messages.showErrorDialog(e.getMessage(), "生成DAO错误");
            throw new RuntimeException(e);
        }
    }

    private DaoGenerateContext getContext() {
        DaoGenerateContext context = new DaoGenerateContext();
        context.tableName = currTableName;
        context.entityName = CamelCaseConverter.toCamelCase(currTableName, false);
        context.lowEntityName = CamelCaseConverter.toCamelCase(currTableName, true);
        context.fieldMap = getFieldMap();
        context.fields = context.fieldMap.values().stream().toList();
        context.primaryFields = context.fields.stream().filter(DbFieldInfo::isPrimaryKey).toList();
        context.options = getOptions();
        return context;
    }

    private Map<String, Boolean> getOptions() {
        MainPanel mainPanel = PluginConfig.mainPanel;
        Map<String, Boolean> optionMap = new HashMap<>();
        optionMap.put(DaoOption.FULL_CONSTRUCTOR, mainPanel.ormFullParamConstructRadioButton.isSelected());
        optionMap.put(DaoOption.AUTO_INCREMENT, mainPanel.ormAutoIncrementAddRadioButton.isSelected());
        optionMap.put(DaoOption.GET_ENTITY_BY_ALL_PRIMARY_KEY, mainPanel.ormBusinessGetEntityByKeyRadioButton.isSelected());
        optionMap.put(DaoOption.UPDATE_ENTITY, mainPanel.ormBusinessUpdateEntityRadioButton.isSelected());
        optionMap.put(DaoOption.GET_ENTITY_LIST_BY_FIST_PRIMARY_KEY, mainPanel.ormBusinessGetEntitiesByKeyRadioButton.isSelected());
        optionMap.put(DaoOption.UPDATE_ENTITY_BATCH, mainPanel.ormBusinessUpdateEntitiesBatchRadioButton.isSelected());
        // optionMap.put(DaoOption.SKIP_OVERRIDE_BUSINESS, mainPanel.ormBusinessSkipOverrideRadioButton.isSelected());
        return optionMap;
    }

    private void createFileDirectoryIfNotExist(Path filePath) throws IOException {
        if (filePath == null) return;
        Path parentPath = filePath.getParent();
        if (Files.notExists(parentPath)) {
            Files.createDirectories(parentPath);
        }
    }
    private void generateEntity() throws IOException {
        String entityContent = EntityCodeUtil.generateEntityCode();
        String entityPath = PluginConfig.getInstance().entityPath();
        generateFile(entityPath, context.entityName, entityContent);
    }

    private void generateDao() throws IOException {
        String content = InterfaceCodeUtil.generateInterfaceCode();
        String daoPath = PluginConfig.getInstance().daoPath();
        String daoFileName = CommonUtil.CreateInterfaceName(context.entityName);
        generateFile(daoPath, daoFileName, content);
    }

    private void generateDaoImpl() throws IOException {
        String implContent = DaoCodeUtil.generateDaoImplCode();
        String implFileName = CommonUtil.CreateImplName(context.entityName);
        String daoImplPath = PluginConfig.getInstance().daoImplPath();
        generateFile(daoImplPath, implFileName, implContent);
    }

    private void generateFactory() throws IOException {
        String factoryPath = PluginConfig.getInstance().factoryPath();
        String factoryFileName = CommonUtil.CreateFactoryName(context.entityName);
        String factoryContent = FactoryCodeUtil.generateFactoryCode(context.entityName, context.tableName);
        generateFile(factoryPath, factoryFileName, factoryContent);
    }

    private void generateBusiness() throws IOException {
        // if (context.options.get(DaoOption.SKIP_OVERRIDE_BUSINESS)) return;
        String businessPath = PluginConfig.getInstance().businessPath();
        String businessFileName = CommonUtil.CreateBusinessName(context.entityName);
        String businessContent = BusinessCodeUtil.generateBusinessCode();
        generateFile(businessPath, businessFileName, businessContent);
    }

    private void generateFile(Path path, String content) throws IOException {
        Files.writeString(path, content);
    }

    private void generateFile(String path, String fileName, String content) throws IOException {
        Path filePath = Paths.get(path + "/" + fileName + ".java");
        createFileDirectoryIfNotExist(filePath);
        generateFile(filePath, content);
    }
}
